/**
  @file
  HTTP processing for the web server.

  Copyright (c) 2011-2012, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <WebServer.h>


/**
  Get a UTF-8 character from the buffer

  @param [in] pData     The address of the buffer containing the character
  @param [out] ppData   The address to receive the next character address

  @return     The character value

**/
INTN
HttpCharGet (
  IN UINT8 * pData,
  IN UINT8 ** ppData
  )
{
  INTN Data;
  INTN Character;
  INTN Control;
  INTN Mask;

  //
  //  Verify that there is some data left
  //
  if ( NULL == pData ) {
    //
    //  No data to return
    //
    pData = NULL;
    Character = 0;
  }
  else {
    //
    //  Get the first portion of the character
    //
    Character = *pData++;
    Control = Character;
    Mask = 0xc0;

    //
    //  Append the rest of the character
    //
    if ( 0 != ( Control & 0x80 )) {
      while ( 0 != ( Control & 0x40 )) {
        Character &= Mask;
        Mask <<= 5;
        Control <<= 1;
        Character <<= 6;
        Data = *pData++ & 0x3f;
        if ( 0x80 != ( Data & 0xc0 )) {
          //
          //  Invalid character
          //
          pData = NULL;
          Character = 0;
          break;
        }
        Character |= Data & 0x3f;
      }
    }
  }

  //
  //  Return the next character location and the character
  //
  *ppData = pData;
  return Character;
}


/**
  Transmit a portion of the HTTP response

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpFlush (
  IN int SocketFD,
  IN WSDT_PORT * pPort
  )
{
  INTN LengthInBytes;
  UINT8 * pBuffer;
  EFI_STATUS Status;

  DBG_ENTER ( );

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;
  pBuffer = &pPort->TxBuffer[0];
  do {
    //
    //  Attempt to send the data
    //
    LengthInBytes = send ( SocketFD,
                           pBuffer,
                           pPort->TxBytes,
                           0 );
    if ( -1 != LengthInBytes ) {
      //
      //  Account for the data sent
      //
      pBuffer += LengthInBytes;
      pPort->TxBytes -= LengthInBytes;
    }
    else {
      //
      //  Transmit error
      //
      Status = EFI_DEVICE_ERROR;
      break;
    }
  } while ( 0 < pPort->TxBytes );

  //
  //  Return the operation status
  //
  DBG_EXIT_STATUS ( Status );
  return Status;
}


/**
  Convert the ANSI character to lower case

  @param [in] Character The character to convert to lower case.

  @return   The lower case character

**/
INTN
HttpLowerCase (
  IN INTN Character
  )
{
  //
  //  Determine if the character is upper case
  //
  if (( 'A' <= Character ) && ( 'Z' >= Character )) {
    Character += 'a' - 'A';
  }

  //
  //  Return the lower case value of the character
  //
  return Character;
}


/**
  Match a Unicode string against a UTF-8 string

  @param [in] pString     A zero terminated Unicode string
  @param [in] pData       A zero terminated UTF-8 string
  @param [in] bIgnoreCase TRUE if case is to be ignored

  @return     The difference between the last two characters tested.
              Returns -1 for error.

**/
INTN
HttpMatch (
  IN UINT16 * pString,
  IN UINT8 * pData,
  IN BOOLEAN bIgnoreCase
  )
{
  INTN Character1;
  INTN Character2;
  INTN Difference;

  do {
    //
    //  Get the character from the comparison string
    //
    Character1 = *pString++;

    //
    //  Convert the character to lower case
    //
    if ( bIgnoreCase ) {
      Character1 = HttpLowerCase ( Character1 );
    }

    //
    //  Get the character from the request
    //
    Character2 = HttpCharGet ( pData, &pData );
    if ( NULL == pData ) {
       //
       // Error getting character
       //
       Difference = -1;
       break;
    }

    //
    //  Convert the character to lower case
    //
    if ( bIgnoreCase ) {
      Character2 = HttpLowerCase ( Character2 );
    }

    //
    //  Compare the characters
    //
    Difference = Character1 - Character2;
    if ( 0 != Difference ) {
      return Difference;
    }
  } while ( 0 != Character1 );

  //
  //  Return the difference
  //
  return Difference;
}


/**
  Buffer the HTTP page header

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [in] pTitle        A zero terminated Unicode title string

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpPageHeader (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN CONST CHAR16 * pTitle
  )
{
  EFI_STATUS Status;

  DBG_ENTER ( );

  //
  //  Build the page header
  //
  for ( ; ; ) {
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  "<!DOCTYPE "
                                  "HTML "
                                  "PUBLIC "
                                  "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
                                  "\"http://www.w3.org/TR/html4/loose.dtd\">\r\n" );
    if ( EFI_ERROR ( Status )) {
      break;
    }
    Status = HttpSendAnsiString ( SocketFD, pPort, "<html lang=\"en-US\">\r\n" );
    if ( EFI_ERROR ( Status )) {
      break;
    }
    if ( NULL != pTitle ) {
      Status = HttpSendAnsiString ( SocketFD, pPort, "  <head>\r\n" );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendAnsiString ( SocketFD, pPort, "    <title>" );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendUnicodeString ( SocketFD, pPort, pTitle );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendAnsiString ( SocketFD, pPort, "</title>\r\n" );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendAnsiString ( SocketFD, pPort, "  </head>\r\n" );
      if ( EFI_ERROR ( Status )) {
        break;
      }
    }
    Status = HttpSendAnsiString ( SocketFD, pPort, "  <body>\r\n" );
    break;
  }

  //
  //  Return the operation status
  //
  DBG_EXIT_STATUS ( Status );
  return Status;
}


/**
  Respond with an error indicating that the page was not found

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [out] pbDone       Address to receive the request completion status

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpPageNotFound (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN BOOLEAN * pbDone
  )
{
  EFI_STATUS Status;

  DBG_ENTER ( );

  //
  //  Send the page not found
  //
  for ( ; ; ) {
    //
    //  Send the page header
    //
    Status = HttpPageHeader ( SocketFD, pPort, L"404 Not found" );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Send the page body
    //
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  "ERROR <b>404</b><br />"
                                  "Requested page is not available\r\n" );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Send the page trailer
    //
    Status = HttpPageTrailer ( SocketFD, pPort, pbDone );
    break;
  }

  //
  //  Return the operation status
  //
  DBG_EXIT_STATUS ( Status );
  return Status;
}


/**
  Buffer and send the HTTP page trailer

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [out] pbDone       Address to receive the request completion status

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpPageTrailer (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN BOOLEAN * pbDone
  )
{
  int RetVal;
  EFI_STATUS Status;
  socklen_t LengthInBytes;
  struct sockaddr_in6 LocalAddress;
  struct sockaddr_in6 RemoteAddress;

  DBG_ENTER ( );

  //
  //  Build the page header
  //
  for ( ; ; ) {
    LengthInBytes = sizeof ( LocalAddress );
    RetVal = getsockname ( SocketFD, (struct sockaddr *)&LocalAddress, &LengthInBytes );
    if ( 0 == RetVal ) {
      LengthInBytes = sizeof ( LocalAddress );
      RetVal = getpeername ( SocketFD, (struct sockaddr *)&RemoteAddress, &LengthInBytes );
      if ( 0 == RetVal ) {
        //
        //  Seperate the body from the trailer
        //
        Status = HttpSendAnsiString ( SocketFD, pPort, "  <hr>\r\n<code>" );
        if ( EFI_ERROR ( Status )) {
          break;
        }

        //
        //  Display the system addresses and the page transfer direction
        //
        Status = HttpSendIpAddress ( SocketFD, pPort, &LocalAddress );
        if ( EFI_ERROR ( Status )) {
          break;
        }
        Status = HttpSendAnsiString ( SocketFD, pPort, "  -->  " );
        if ( EFI_ERROR ( Status )) {
          break;
        }
        Status = HttpSendIpAddress ( SocketFD, pPort, &RemoteAddress );
        if ( EFI_ERROR ( Status )) {
          break;
        }
        Status = HttpSendAnsiString ( SocketFD, pPort, "</code>\r\n" );
        if ( EFI_ERROR ( Status )) {
          break;
        }
      }
    }

    //
    //  Terminate the page
    //
    Status = HttpSendAnsiString ( SocketFD, pPort, "  </body>\r\n" );
    if ( EFI_ERROR ( Status )) {
      break;
    }
    Status = HttpSendAnsiString ( SocketFD, pPort, "  </html>\r\n" );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Send the page trailer
    //
    Status = HttpFlush ( SocketFD, pPort );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Mark the page as complete
    //
    *pbDone = TRUE;
    break;
  }

  //
  //  Return the operation status
  //
  DBG_EXIT_STATUS ( Status );
  return Status;
}


/**
  Replace a space with a zero

  @param [in] pData     The request buffer address
  @param [in] pEnd      End of buffer address

  @return     The next character location

**/
UINT8 *
HttpReplaceSpace (
  IN UINT8 * pData,
  IN UINT8 * pEnd
  )
{
  INTN Character;
  UINT8 * pSpace;

  pSpace = pData;
  while ( pEnd > pData ) {
    //
    //  Get the character from the request
    //
    Character = HttpCharGet ( pData, &pData );
    if ( ' ' == Character ) {
      break;
    }
    pSpace = pData;
  }

  //
  //  Replace the space character with zero
  //
  ZeroMem ( pSpace, pData - pSpace );

  //
  //  Return the next character location
  //
  return pData;
}


/**
  Process an HTTP request

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [out] pbDone       Address to receive the request completion status

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpRequest (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  OUT BOOLEAN * pbDone
  )
{
  UINT8 * pData;
  UINT8 * pEnd;
  CONST DT_PAGE * pPage;
  CONST DT_PAGE * pPageEnd;
  UINT8 * pVerb;
  UINT8 * pVersion;
  UINT8 * pWebPage;
  EFI_STATUS Status;

  DBG_ENTER ( );

  //
  //  Assume the request is not finished
  //
  *pbDone = FALSE;
  Status = EFI_SUCCESS;
  for ( ; ; ) {

    //
    //  Attempt to parse the command
    //
    pData = &pPort->Request[0];
    pEnd = &pData[ pPort->RequestLength ];
    pVerb = pData;
    pWebPage = HttpReplaceSpace ( pVerb, pEnd );
    if ( pEnd <= pWebPage ) {
      break;
    }
    pVersion = HttpReplaceSpace ( pWebPage, pEnd );
    if ( pEnd <= pVersion ) {
      break;
    }

    //
    //  Validate the request
    //
    if ( 0 != HttpMatch ( L"GET", pVerb, TRUE )) {
      //
      //  Invalid request type
      //
      DEBUG (( DEBUG_REQUEST,
                "HTTP: Invalid verb\r\n" ));
      Status = EFI_NOT_FOUND;
      break;
    }

    //
    //  Walk the page table
    //
    pPage = &mPageList[0];
    pPageEnd = &pPage[ mPageCount ];
    while ( pPageEnd > pPage ) {
      //
      //  Determine if the page was located
      //
      if ( 0 == HttpMatch ( pPage->pPageName, pWebPage, FALSE )) {
        break;
      }

      //
      //  Set the next page
      //
      pPage += 1;
    }
    if ( pPageEnd <= pPage ) {
      //
      //  The page was not found
      //
      DEBUG (( DEBUG_REQUEST,
                "HTTP: Page not found in page table\r\n" ));
      Status = EFI_NOT_FOUND;
      break;
    }

    //
    //  Respond with the page contents
    //
    Status = pPage->pfnResponse ( SocketFD, pPort, pbDone );
    break;
  }

  //
  //  Return page not found if necessary
  //
  if ( EFI_NOT_FOUND == Status ) {
    Status = HttpPageNotFound ( SocketFD, pPort, pbDone );
  }

  //
  //  Return the operation status
  //
  DBG_EXIT_STATUS ( Status );
  return Status;
}


/**
  Buffer data for sending

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [in] LengthInBytes Length of valid data in the buffer
  @param [in] pBuffer       Buffer of data to send

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpSend (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN size_t LengthInBytes,
  IN CONST UINT8 * pBuffer
  )
{
  size_t DataBytes;
  size_t MaxBytes;
  EFI_STATUS Status;

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;
  do {
    //
    //  Determine how much data fits into the buffer
    //
    MaxBytes = sizeof ( pPort->TxBuffer );
    DataBytes = MaxBytes - pPort->TxBytes;
    if ( DataBytes > LengthInBytes ) {
      DataBytes = LengthInBytes;
    }

    //
    //  Copy the data into the buffer
    //
    CopyMem ( &pPort->TxBuffer[ pPort->TxBytes ],
              pBuffer,
              DataBytes );

    //
    //  Account for the data copied
    //
    pPort->TxBytes += DataBytes;
    LengthInBytes -= DataBytes;

    //
    //  Transmit the buffer if it is full
    //
    if ( MaxBytes <= pPort->TxBytes ) {
      Status = HttpFlush ( SocketFD, pPort );
    }
  } while (( EFI_SUCCESS == Status ) && ( 0 < LengthInBytes ));

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Send an ANSI string

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [in] pString       A zero terminated Unicode string

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpSendAnsiString (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN CONST char * pString
  )
{
  CONST char * pData;
  EFI_STATUS Status;

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;

  //
  //  Walk the characters in he string
  //
  pData = pString;
  while ( 0 != *pData ) {
    pData += 1;
  }

  //
  //  Send the string
  //
  Status = HttpSend ( SocketFD,
                      pPort,
                      pData - pString,
                      (CONST UINT8 *)pString );

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Buffer a single byte

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [in] Data          The data byte to send

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpSendByte (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN UINT8 Data
  )
{
  EFI_STATUS Status;

  //
  //  Send the data byte
  //
  Status = HttpSend ( SocketFD,
                      pPort,
                      1,
                      &Data );

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Display a character

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [in] Character     Character to display
  @param [in] pReplacement  Replacement character string

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpSendCharacter (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN CHAR8 Character,
  IN CHAR8 * pReplacement
  )
{
  EFI_STATUS Status;

  //
  //  Determine if this is a printable character
  //
  if (( 0x20 <= Character ) && ( 0x7f > Character )) {
    if ( '<' == Character ) {
      //
      //  Replace with HTML equivalent
      //
      Status = HttpSendAnsiString ( SocketFD,
                                    pPort,
                                    "&lt;" );
    }
    else if ( '>' == Character ) {
      //
      //  Replace with HTML equivalent
      //
      Status = HttpSendAnsiString ( SocketFD,
                                    pPort,
                                    "&gt;" );
    }
    else if ( '&' == Character ) {
      //
      //  Replace with HTML equivalent
      //
      Status = HttpSendAnsiString ( SocketFD,
                                    pPort,
                                    "&amp;" );
    }
    else if ( '\"' == Character ) {
      //
      //  Replace with HTML equivalent
      //
      Status = HttpSendAnsiString ( SocketFD,
                                    pPort,
                                    "&quot;" );
    }
    else {
      //
      //  Display the character
      //
      Status = HttpSendByte ( SocketFD,
                              pPort,
                              Character );
    }
  }
  else {
    //
    //  Not a displayable character
    //
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  pReplacement );
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Send a buffer dump

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [in] ByteCount     The number of bytes to display
  @param [in] pData         Address of the byte array

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpSendDump (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN UINTN ByteCount,
  IN CONST UINT8 * pData
  )
{
  INTN BytesToDisplay;
  UINT8 Character;
  INTN Index;
  INTN InitialSpaces;
  CONST UINT8 * pDataEnd;
  CONST UINT8 * pEnd;
  CONST UINT8 * pTemp;
  EFI_STATUS Status;

  //
  //  Use for/break instead of goto
  //
  for ( ; ; ) {
    //
    //  Start the field value
    //
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  "<code>" );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Walk the bytes to be displayed
    //
    pEnd = &pData[ ByteCount ];
    while ( pEnd > pData ) {
      //
      //  Display the address
      //
      Status = HttpSendHexBits ( SocketFD,
                                 pPort,
                                 sizeof ( pData ) * 8,
                                 (UINT64)(UINTN)pData );
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Separate the address and data
      //
      Status = HttpSendByte ( SocketFD, pPort, ':' );
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Position the starting data correctly
      //
      InitialSpaces = (UINTN)pData;
      InitialSpaces &= BYTES_ON_A_LINE - 1;
      for ( Index = SPACES_ADDRESS_TO_DATA
                  + (( 2 + SPACES_BETWEEN_BYTES )
                        * InitialSpaces );
            0 < Index; Index-- ) {
        Status = HttpSendAnsiString ( SocketFD,
                                      pPort,
                                      "&nbsp;" );
        if ( EFI_ERROR ( Status )) {
          break;
        }
      }
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Display the data
      //
      BytesToDisplay = pEnd - pData;
      if (( BYTES_ON_A_LINE - InitialSpaces ) < BytesToDisplay ) {
        BytesToDisplay = BYTES_ON_A_LINE - InitialSpaces;
      }
      pDataEnd = &pData[ BytesToDisplay ];
      pTemp = pData;
      while ( pDataEnd > pTemp ) {
        Status = HttpSendHexBits ( SocketFD,
                                   pPort,
                                   8,
                                   *pTemp++ );
        if ( EFI_ERROR ( Status )) {
          break;
        }

        //
        //  Separate the data bytes
        //
        for ( Index = SPACES_BETWEEN_BYTES; 0 < Index; Index-- ) {
          Status = HttpSendAnsiString ( SocketFD,
                                        pPort,
                                        "&nbsp;" );
          if ( EFI_ERROR ( Status )) {
            break;
          }
        }
        if ( EFI_ERROR ( Status )) {
          break;
        }
      }
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Separate the data from the ASCII display
      //
      for ( Index = (( 2 + SPACES_BETWEEN_BYTES )
                       * ( BYTES_ON_A_LINE - BytesToDisplay - InitialSpaces ))
                  - SPACES_BETWEEN_BYTES
                  + SPACES_DATA_TO_ASCII
                  + InitialSpaces;
            0 < Index; Index-- ) {
        Status = HttpSendAnsiString ( SocketFD,
                                      pPort,
                                      "&nbsp;" );
        if ( EFI_ERROR ( Status )) {
          break;
        }
      }
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Display the ASCII data
      //
      while ( pDataEnd > pData ) {
        Character = *pData++;
        Status = HttpSendCharacter ( SocketFD,
                                     pPort,
                                     Character,
                                     "." );
        if ( EFI_ERROR ( Status )) {
          break;
        }
      }
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Terminate the line
      //
      Status = HttpSendAnsiString ( SocketFD,
                                    pPort,
                                    "<br/>\r\n" );
      if ( EFI_ERROR ( Status )) {
        break;
      }
    }

    //
    //  Terminate the field value and row
    //
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  "</code>\r\n" );
    break;
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Display a row containing a GUID value

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [in] pGuid         Address of the GUID to display

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpSendGuid (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN CONST EFI_GUID * pGuid
  )
{
  UINT32 Index;
  EFI_STATUS Status;

  DBG_ENTER ( );

  //
  //  Use for/break instead of goto
  //
  for ( ; ; ) {
    //
    //  Display the GUID in a form found in the code
    //
    //  E.g. 0xca16005f, 0x11ec, 0x4bdc, { 0x99, 0x97, 0x27, 0x2c, 0xa9, 0xba, 0x15, 0xe5 }
    //

    //
    //  Display the first 32 bits
    //
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  "0x" );
    if ( EFI_ERROR ( Status )) {
      break;
    }
    Status = HttpSendHexBits ( SocketFD,
                               pPort,
                               32,
                               pGuid->Data1 );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Display the second 16 bits
    //
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  ", 0x" );
    if ( EFI_ERROR ( Status )) {
      break;
    }
    Status = HttpSendHexBits ( SocketFD,
                               pPort,
                               16,
                               pGuid->Data2 );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Display the thrid 16 bits
    //
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  ", 0x" );
    if ( EFI_ERROR ( Status )) {
      break;
    }
    Status = HttpSendHexBits ( SocketFD,
                               pPort,
                               16,
                               pGuid->Data3 );
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Place the last 64 bits in braces
    //
    Status = HttpSendAnsiString ( SocketFD,
                                  pPort,
                                  ", { 0x" );
    if ( EFI_ERROR ( Status )) {
      break;
    }
    for ( Index = 0; 7 >= Index; Index++ ) {
      //
      //  Display the next 8 bits
      //
      Status = HttpSendHexBits ( SocketFD,
                                 pPort,
                                 8,
                                 pGuid->Data4[ Index ]);
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Separate the bytes
      //
      Status = HttpSendAnsiString ( SocketFD,
                                    pPort,
                                    ( 7 != Index ) ? ", 0x" : " }" );
      if ( EFI_ERROR ( Status )) {
        break;
      }
    }
    break;
  }

  //
  //  Return the operation status
  //
  DBG_EXIT_STATUS ( Status );
  return Status;
}


/**
  Output a hex value to the HTML page

  @param [in] SocketFD    Socket file descriptor
  @param [in] pPort       The WSDT_PORT structure address
  @param [in] Bits        Number of bits to display
  @param [in] Value       Value to display

  @retval EFI_SUCCESS Successfully displayed the address
**/
EFI_STATUS
HttpSendHexBits (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN INT32 Bits,
  IN UINT64 Value
  )
{
  UINT32 Digit;
  INT32 Shift;
  EFI_STATUS Status;

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;

  //
  //  Walk the list of divisors
  //
  Shift = (( Bits + 3 ) & ( ~3 )) - 4;
  while ( 0 <= Shift ) {
    //
    //  Determine the next digit
    //
    Digit = (UINT32)(( Value >> Shift ) & 0xf );
    if ( 10 <= Digit ) {
      Digit += 'a' - '0' - 10;
    }

    //
    //  Display the digit
    //
    Status = HttpSendByte ( SocketFD, pPort, (UINT8)( '0' + Digit ));
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Set the next shift
    //
    Shift -= 4;
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Output a hex value to the HTML page

  @param [in] SocketFD    Socket file descriptor
  @param [in] pPort       The WSDT_PORT structure address
  @param [in] Value       Value to display

  @retval EFI_SUCCESS Successfully displayed the address
**/
EFI_STATUS
HttpSendHexValue (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN UINT64 Value
  )
{
  BOOLEAN bDisplayZeros;
  UINT32 Digit;
  INT32 Shift;
  EFI_STATUS Status;

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;

  //
  //  Walk the list of divisors
  //
  bDisplayZeros = FALSE;
  Shift = 60;
  do {
    //
    //  Determine the next digit
    //
    Digit = (UINT32)(( Value >> Shift ) & 0xf );
    if ( 10 <= Digit ) {
      Digit += 'a' - '0' - 10;
    }

    //
    //  Suppress leading zeros
    //
    if (( 0 != Digit ) || bDisplayZeros || ( 0 == Shift )) {
      bDisplayZeros = TRUE;

      //
      //  Display the digit
      //
      Status = HttpSendByte ( SocketFD, pPort, (UINT8)( '0' + Digit ));
      if ( EFI_ERROR ( Status )) {
        break;
      }
    }

    //
    //  Set the next shift
    //
    Shift -= 4;
  } while ( 0 <= Shift );

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Output an IP6 address value to the HTML page

  @param [in] SocketFD          Socket file descriptor
  @param [in] pPort             The WSDT_PORT structure address
  @param [in] Value             Value to display
  @param [in] bFirstValue       TRUE if first value
  @param [in] bLastValue        TRUE if last value
  @param [in] bZeroSuppression  TRUE while zeros are being suppressed
  @param [in] pbZeroSuppression Address to receive TRUE when zero suppression
                                has started, use NULL if next colon value not
                                needed.

  @retval EFI_SUCCESS Successfully displayed the address
**/
EFI_STATUS
HttpSendIp6Value (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN UINT16 Value,
  IN BOOLEAN bFirstValue,
  IN BOOLEAN bLastValue,
  IN BOOLEAN bZeroSuppression,
  IN BOOLEAN * pbZeroSuppression
  )
{
  BOOLEAN bZeroSuppressionStarting;
  UINT32 Digit;
  EFI_STATUS Status;

  //
  //  Use break instead of goto
  //
  bZeroSuppressionStarting = FALSE;
  Status = EFI_SUCCESS;
  for ( ; ; ) {
    //
    //  Display the leading colon if necessary
    //
    if ( bZeroSuppression && ( bLastValue || ( 0 != Value ))) {
      Status = HttpSendByte ( SocketFD, pPort, ':' );
      if ( EFI_ERROR ( Status )) {
        break;
      }
    }

    //
    //  Skip over a series of zero values
    //
    bZeroSuppressionStarting = (BOOLEAN)( 0 == Value );
    if ( !bZeroSuppressionStarting ) {
      //
      //  Display the value
      //
      Digit = ( Value >> 4 ) & 0xf;
      Status = HttpSendHexValue ( SocketFD,
                                  pPort,
                                  Digit );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Digit = Value & 0xf;
      Status = HttpSendHexValue ( SocketFD,
                                  pPort,
                                  Digit );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Digit = ( Value >> 12 ) & 0xf;
      Status = HttpSendHexValue ( SocketFD,
                                  pPort,
                                  Digit );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Digit = ( Value >> 8 ) & 0xf;
      Status = HttpSendHexValue ( SocketFD,
                                  pPort,
                                  Digit );
      if ( EFI_ERROR ( Status )) {
        break;
      }
    }

    //
    //  Display the trailing colon if necessary
    //
    if (( !bLastValue ) && ( bFirstValue || ( 0 != Value ))) {
      Status = HttpSendByte ( SocketFD, pPort, ':' );
    }
    break;
  }

  //
  //  Return the next colon display
  if ( NULL != pbZeroSuppression ) {
    *pbZeroSuppression = bZeroSuppressionStarting;
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Output an IP address to the HTML page

  @param [in] SocketFD    Socket file descriptor
  @param [in] pPort       The WSDT_PORT structure address
  @param [in] pAddress    Address of the socket address

  @retval EFI_SUCCESS Successfully displayed the address
**/
EFI_STATUS
HttpSendIpAddress (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN struct sockaddr_in6 * pAddress
  )
{
  BOOLEAN bZeroSuppression;
  UINT32 Index;
  struct sockaddr_in * pIpv4;
  struct sockaddr_in6 * pIpv6;
  UINT16 PortNumber;
  EFI_STATUS Status;

  //
  //  Use break instead of goto
  //
  for ( ; ; ) {
    //
    //  Determine the type of address
    //
    if ( AF_INET6 == pAddress->sin6_family ) {
      pIpv6 = pAddress;

      //
      //  Display the address in RFC2732 format
      //
      bZeroSuppression = FALSE;
      Status = HttpSendByte ( SocketFD, pPort, '[' );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      for ( Index = 0; 8 > Index; Index++ ) {
        Status = HttpSendIp6Value ( SocketFD,
                                    pPort,
                                    pIpv6->sin6_addr.__u6_addr.__u6_addr16[ Index ],
                                    (BOOLEAN)( 0 == Index ),
                                    (BOOLEAN)( 7 == Index ),
                                    bZeroSuppression,
                                    &bZeroSuppression );
        if ( EFI_ERROR ( Status )) {
          break;
        }
      }
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Separate the port number
      //
      Status = HttpSendByte ( SocketFD, pPort, ']' );

      //
      //  Get the port number
      //
      PortNumber = pIpv6->sin6_port;
    }
    else {
      //
      //  Output the IPv4 address
      //
      pIpv4 = (struct sockaddr_in *)pAddress;
      Status = HttpSendValue ( SocketFD, pPort, (UINT8)pIpv4->sin_addr.s_addr );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendByte ( SocketFD, pPort, '.' );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendValue ( SocketFD, pPort, (UINT8)( pIpv4->sin_addr.s_addr >> 8 ));
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendByte ( SocketFD, pPort, '.' );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendValue ( SocketFD, pPort, (UINT8)( pIpv4->sin_addr.s_addr >> 16 ));
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendByte ( SocketFD, pPort, '.' );
      if ( EFI_ERROR ( Status )) {
        break;
      }
      Status = HttpSendValue ( SocketFD, pPort, (UINT8)( pIpv4->sin_addr.s_addr >> 24 ));

      //
      //  Get the port number
      //
      PortNumber = pIpv4->sin_port;
    }
    if ( EFI_ERROR ( Status )) {
      break;
    }

    //
    //  Display the port number
    //
    Status = HttpSendByte ( SocketFD, pPort, ':' );
    if ( EFI_ERROR ( Status )) {
      break;
    }
    Status = HttpSendValue ( SocketFD, pPort, htons ( PortNumber ));
    break;
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Send a Unicode string

  @param [in] SocketFD      The socket's file descriptor to add to the list.
  @param [in] pPort         The WSDT_PORT structure address
  @param [in] pString       A zero terminated Unicode string

  @retval EFI_SUCCESS       The request was successfully processed

**/
EFI_STATUS
HttpSendUnicodeString (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN CONST UINT16 * pString
  )
{
  UINT8 Data;
  UINT16 Character;
  EFI_STATUS Status;

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;

  //
  //  Walk the characters in he string
  //
  while ( 0 != ( Character = *pString++ )) {
    //
    //  Convert the character to UTF-8
    //
    if ( 0 != ( Character & 0xf800 )) {
      //
      //  Send the upper 4 bits
      //
      Data = (UINT8)(( Character >> 12 ) & 0xf );
      Data |= 0xe0;
      Status = HttpSendByte ( SocketFD,
                              pPort,
                              Data );
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Send the next 6 bits
      //
      Data = (UINT8)(( Character >> 6 ) & 0x3f );
      Data |= 0x80;
      Status = HttpSendByte ( SocketFD,
                              pPort,
                              Data );
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Send the last 6 bits
      //
      Data = (UINT8)( Character & 0x3f );
      Data |= 0x80;
    }
    else if ( 0 != ( Character & 0x0780 )) {
      //
      //  Send the upper 5 bits
      //
      Data = (UINT8)(( Character >> 6 ) & 0x1f );
      Data |= 0xc0;
      Status = HttpSendByte ( SocketFD,
                              pPort,
                              Data );
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Send the last 6 bits
      //
      Data = (UINT8)( Character & 0x3f );
      Data |= 0x80;
    }
    else {
      Data = (UINT8)( Character & 0x7f );
    }

    //
    //  Send the last data byte
    //
    Status = HttpSendByte ( SocketFD,
                            pPort,
                            Data );
    if ( EFI_ERROR ( Status )) {
      break;
    }
  }

  //
  //  Return the operation status
  //
  return Status;
}


/**
  Output a value to the HTML page

  @param [in] SocketFD    Socket file descriptor
  @param [in] pPort       The WSDT_PORT structure address
  @param [in] Value       Value to display

  @retval EFI_SUCCESS Successfully displayed the address
**/
EFI_STATUS
HttpSendValue (
  IN int SocketFD,
  IN WSDT_PORT * pPort,
  IN UINT64 Value
  )
{
  BOOLEAN bDisplayZeros;
  UINT64 Digit;
  CONST UINT64 * pEnd;
  CONST UINT64 * pDivisor;
  CONST UINT64 pDivisors[ ] = {
     10000000000000000000ULL,
      1000000000000000000ULL,
       100000000000000000ULL,
        10000000000000000ULL,
         1000000000000000ULL,
          100000000000000ULL,
           10000000000000ULL,
            1000000000000ULL,
             100000000000ULL,
              10000000000ULL,
               1000000000ULL,
                100000000ULL,
                 10000000ULL,
                  1000000ULL,
                   100000ULL,
                    10000ULL,
                     1000ULL,
                      100ULL,
                       10ULL
  };
  EFI_STATUS Status;
  UINT64 Temp;

  //
  //  Assume success
  //
  Status = EFI_SUCCESS;

  //
  //  Walk the list of divisors
  //
  bDisplayZeros = FALSE;
  pDivisor = &pDivisors[0];
  pEnd = &pDivisor[ sizeof ( pDivisors ) / sizeof ( pDivisors[0])];
  while ( pEnd > pDivisor ) {
    //
    //  Determine the next digit
    //
    Digit = Value / *pDivisor;

    //
    //  Suppress leading zeros
    //
    if (( 0 != Digit ) || bDisplayZeros ) {
      bDisplayZeros = TRUE;

      //
      //  Display the digit
      //
      Status = HttpSendByte ( SocketFD, pPort, (UINT8)( '0' + Digit ));
      if ( EFI_ERROR ( Status )) {
        break;
      }

      //
      //  Determine the remainder
      //
      Temp = *pDivisor * Digit;
      Value -= Temp;
    }

    //
    //  Set the next divisor
    //
    pDivisor += 1;
  }

  //
  //  Display the final digit
  //
  if ( !EFI_ERROR ( Status )) {
    Status = HttpSendByte ( SocketFD, pPort, (UINT8)( '0' + Value ));
  }

  //
  //  Return the operation status
  //
  return Status;
}
